#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <ctype.h>

#define LOG_FILE "run-log"
#define MAX_LINE_LENGTH 100

// tm.tm_hour
// tm.tm_min
// tm.tm_sec
struct tm* get_time() {
  time_t epoch_time;
  struct tm* tm_p;
  epoch_time = time(NULL);
  tm_p = gmtime(&epoch_time);
  return tm_p;
}

// ts.tv_sec
// ts.tv_nsec
long long get_time_ns() {
  struct timespec ts;
  clock_gettime(CLOCK_REALTIME, &ts);
  long long time = ((long long)ts.tv_sec) * 1000000000 + ts.tv_nsec;
  return time;
}

typedef struct line_list_s {
  struct line_list_s* next; 
  char* line;
} line_list_t;

void print_line_list(line_list_t* list);
char* get_line_from_list(line_list_t* list);

line_list_t* get_file_lines(char* filename) {
  line_list_t* head = NULL;
  line_list_t* prev;
  line_list_t* cur;
  char line[MAX_LINE_LENGTH+1];
  int i;
  int line_num = 0;
  FILE* file = fopen(filename, "r");
  if (file != NULL) {
    while (fgets(line, MAX_LINE_LENGTH+1, file) != NULL) {
      
      // Only store characters up to the first whitespace/newline
      for (i = 0; line[i]; i++) {
	      //printf("%c: is space: %d\n", line[i], isspace(line[i]));
        if (isspace(line[i])) {
          line[i] = '\0';
          break;
        }
      }

      // Skip blank lines
      if (i == 0) {
        continue;
      }

      //if (line_num > 0) {
      //  print_line_list(head);
      //} 

      // Add line to linked list
      cur = (line_list_t*)malloc(sizeof(line_list_t));
      cur->line = malloc(sizeof(char)*i);
      strcpy(cur->line, line);


      if (head == NULL) {
        head = cur;
      }
      else {
        prev->next = cur;
      }
      prev = cur;

      line_num++;
      //print_line_list(head);
      //printf("\n");
      //if (line_num == 3) break;
    }
    cur->next = NULL;
    fclose(file);

    cur = head;
    while (cur) {
      get_line_from_list(cur);
      cur = cur->next;
    }

    return head;
  }
  return NULL;
}

void print_line_list(line_list_t* list) {
  line_list_t* cur = list;
  int i = 0;
  while (cur) {
    printf("%d: %s\n", i, cur->line);
    //for (i = 0; cur->line[i]; i++) {
    //  printf("%c: %d\n", cur->line[i], (int)cur->line[i]);
    //}
    cur = cur->next;
    i++;
  }
}

char* get_line_from_list(line_list_t* list) {
  int i;
  if (list) {
    for (i = 0; list->line[i]; i++) {
      if (list->line[i] == 17) {
        list->line[i] = 0;
      }
    }
    return list->line;
  }
  return NULL;
}

void run_tests_on_node_and_ip(FILE* log, char* dst_ip, char* node, char* ip) {
  int return_code;
  char command[255];
  
  fprintf(log, "Running tests on node %s from IP %s\n", node, ip);
  
  sprintf(command, "./node_ssh.sh %s 'sudo ./tx TCP 50 `./get_time.sh` `./ip.sh` 80 %s 49340'", node, dst_ip);
  return_code = system(command);
  fprintf(log, "  (non-spoofed) TCP return code %d\n", return_code);
  
  sleep(5);
  
  sprintf(command, "./node_ssh.sh %s 'sudo ./tx UDP 50 `./get_time.sh` `./ip.sh` 80 %s 49340'", node, dst_ip);
  return_code = system(command);
  fprintf(log, "  (non-spoofed) UDP return code %d\n", return_code);
  
  sleep(5);
  
  sprintf(command, "./node_ssh.sh %s 'sudo ./tx TCP 50 `./get_time.sh` %s 80 %s 49340'", node, ip, dst_ip);
  return_code = system(command);
  fprintf(log, "  TCP return code %d\n", return_code);
  
  sleep(5);
  
  sprintf(command, "./node_ssh.sh %s 'sudo ./tx UDP 50 `./get_time.sh` %s 80 %s 49340'", node, ip, dst_ip);
  return_code = system(command);
  fprintf(log, "  UDP return code %d\n", return_code);
}

void run_tests_on_node(FILE* log, char* dst_ip, char* node, line_list_t* ips) {
  line_list_t* cur_ip = ips;
  while (cur_ip) {
    run_tests_on_node_and_ip(log, dst_ip, node, cur_ip->line);
    
    cur_ip = cur_ip->next;
    
    // Wait a few seconds before moving on to the next IP
    sleep(5);
  }
}

// For each node
// For each IP
// Send packets from node that appear to be from IP
// Wait a little while
// Move on to the next IP/node
void run_tests(FILE* log, char* dst_ip, line_list_t* nodes, line_list_t* ips) {
  line_list_t* cur_node;
  
  // Repeat tests until termination
  while (1) {
    // Test all IPs from each node
    cur_node = nodes;
    
    while (cur_node) {
      run_tests_on_node(log, dst_ip, cur_node->line, ips);
      cur_node = cur_node->next;
      
      // Wait a few seconds before moving on to the next node
      sleep(15);
    }
  }
}

// args: 1: file with planetlab nodes
//       2: file with IPs
int main(int argc, char** argv) {
  if (argc < 4) {
    fprintf(stderr, "Usage: ./run dst_ip nodes.txt ips.txt\n");
    exit(1);
  }

  FILE* log = fopen(LOG_FILE, "a");
  
  line_list_t* nodes = get_file_lines(argv[2]);
  if (nodes == NULL) {
    fprintf(stderr, "Could not read nodes file %s\n", argv[2]);
    exit(1);
  }
  print_line_list(nodes);
  
  line_list_t* ips = get_file_lines(argv[3]);
  if (ips == NULL) {
    fprintf(stderr, "Could not read ips file %s\n", argv[3]);
    exit(1);
  }
  //print_line_list(ips);
  
  // Real:
  run_tests(log, argv[1], nodes, ips);
  
  // For testing:
  //char* node="planetlab5.cs.uiuc.edu";
  //char* ip="50.38.0.159";
  //run_tests_on_node_and_ip(log, argv[1], node, ip);
  
  fclose(log);
  return 0;
}
